package com.nbsaas.boot.script;

import javax.script.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用热加载脚本引擎
 * 支持所有 JSR-223 脚本语言：
 * - 如果引擎支持 Compilable，则缓存编译结果
 * - 如果引擎不支持 Compilable，则每次直接解释执行
 */
public class UniversalHotReloadScriptEngine {

    private final Map<String, CachedScript> cache = new ConcurrentHashMap<>();
    private final Map<String, ScriptSource> sources = new HashMap<>();
    private final ScriptEngine engine;
    private final boolean isCompilable;

    public UniversalHotReloadScriptEngine(String language) {
        // 加载脚本源
        ServiceLoader<ScriptSource> loader = ServiceLoader.load(ScriptSource.class);
        for (ScriptSource source : loader) {
            sources.put(source.getPrefix(), source);
            System.out.println("加载脚本源: " + source.getPrefix());
        }

        // 初始化脚本引擎
        ScriptEngineManager factory = new ScriptEngineManager();
        this.engine = factory.getEngineByName(language);
        if (this.engine == null) {
            throw new IllegalStateException("未找到脚本引擎: " + language);
        }

        // 判断是否支持 Compilable
        this.isCompilable = engine instanceof Compilable;
        if (!isCompilable) {
            System.out.println("警告: 脚本引擎不支持 Compilable，将直接解释执行");
        }
    }

    public Object execute(String path) throws Exception {
        return execute(path, null);
    }

    public Object execute(String path, Map<String, Object> context) throws Exception {
        ScriptSource source = resolve(path);
        String script = source.loadScript(path);

        if (isCompilable) {
            // 支持 Compilable，则缓存编译结果
            CachedScript cached = cache.get(path);
            if (cached == null) {
                CompiledScript compiled = ((Compilable) engine).compile(script);
                cached = new CachedScript(compiled, System.currentTimeMillis());
                cache.put(path, cached);
            }
            Bindings bindings = createBindings(cached.script.getEngine(), context);
            return cached.script.eval(bindings);
        } else {
            // 不支持 Compilable，每次直接解释执行
            Bindings bindings = createBindings(engine, context);
            return engine.eval(script, bindings);
        }
    }

    private Bindings createBindings(ScriptEngine engine, Map<String, Object> context) {
        Bindings bindings = engine.createBindings();
        if (context != null) {
            bindings.putAll(context);
        }
        return bindings;
    }

    private ScriptSource resolve(String path) {
        return sources.values().stream()
                .filter(s -> path.startsWith(s.getPrefix()))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("未知路径协议: " + path));
    }

    private record CachedScript(CompiledScript script, long timestamp) {
    }
}
